/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.test.internal.util;
import java.io.IOException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Loads a property file performing key expansion.
*
* Provides simple property substitution, without support for inner or nested placeholders. Also, the algorithm does
* only one parsing so derivative placeholders are not supported.
*
*
* @author Costin Leau
*
*/
public abstract class PropertiesUtil {
private static final String DELIM_START = "${";
private static final String DELIM_STOP = "}";
private static final Properties EMPTY_PROPERTIES = new Properties();
private static final class OrderedProperties extends Properties implements Cloneable {
private final Map<Object, Object> map = new LinkedHashMap<Object, Object>();
/**
*
* @see java.util.Map#clear()
*/
public void clear() {
map.clear();
}
/**
* @param key
* @return
* @see java.util.Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key) {
return map.containsKey(key);
}
/**
* @param value
* @return
* @see java.util.Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value) {
return map.containsValue(value);
}
/**
* @return
* @see java.util.Map#entrySet()
*/
public Set<Entry<Object, Object>> entrySet() {
return map.entrySet();
}
/**
* @param o
* @return
* @see java.util.Map#equals(java.lang.Object)
*/
public boolean equals(Object o) {
return map.equals(o);
}
/**
* @param key
* @return
* @see java.util.Map#get(java.lang.Object)
*/
public Object get(Object key) {
return map.get(key);
}
@Override
public String getProperty(String key) {
Object oval = map.get(key);
String sval = (oval instanceof String) ? (String) oval : null;
return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
}
/**
* @return
* @see java.util.Map#hashCode()
*/
public int hashCode() {
return map.hashCode();
}
/**
* @return
* @see java.util.Map#isEmpty()
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* @return
* @see java.util.Map#keySet()
*/
public Set<Object> keySet() {
return map.keySet();
}
/**
* @param key
* @param value
* @return
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
*/
public Object put(Object key, Object value) {
return map.put(key, value);
}
/**
* @param m
* @see java.util.Map#putAll(java.util.Map)
*/
public void putAll(Map<? extends Object, ? extends Object> m) {
map.putAll(m);
}
/**
* @param key
* @return
* @see java.util.Map#remove(java.lang.Object)
*/
public Object remove(Object key) {
return map.remove(key);
}
/**
* @return
* @see java.util.Map#size()
*/
public int size() {
return map.size();
}
/**
* @return
* @see java.util.Map#values()
*/
public Collection<Object> values() {
return map.values();
}
@Override
public synchronized boolean contains(Object value) {
return map.containsValue(value);
}
@Override
public synchronized Enumeration<Object> elements() {
return new IteratorBackedEnumeration(map.values().iterator());
}
@Override
public synchronized Enumeration<Object> keys() {
return new IteratorBackedEnumeration(map.keySet().iterator());
}
@Override
public synchronized String toString() {
return map.toString();
}
private final class IteratorBackedEnumeration<K> implements Enumeration<K> {
private final Iterator<? extends K> iterator;
public IteratorBackedEnumeration(Iterator<? extends K> iterator) {
this.iterator = iterator;
}
public boolean hasMoreElements() {
return iterator.hasNext();
}
public K nextElement() {
return iterator.next();
}
}
}
/**
* Shortcut method - loads a property object from the given input stream and applies property expansion. The
* returned properties object preserves order at the expense of speed.
*
* @param resource
* @return
*/
public static Properties loadAndExpand(Resource resource) {
Properties props = new OrderedProperties();
if (resource == null)
return props;
try {
props.load(resource.getInputStream());
} catch (IOException ex) {
return null;
}
return expandProperties(props);
}
/**
* Filter/Eliminate keys that start with the given prefix.
*
* @param properties
* @param prefix
* @return
*/
public static Properties filterKeysStartingWith(Properties properties, String prefix) {
if (!StringUtils.hasText(prefix))
return EMPTY_PROPERTIES;
Assert.notNull(properties);
Properties excluded = (properties instanceof OrderedProperties ? new OrderedProperties() : new Properties());
// filter ignore keys out
for (Enumeration enm = properties.keys(); enm.hasMoreElements();) {
String key = (String) enm.nextElement();
if (key.startsWith(prefix)) {
excluded.put(key, properties.get(key));
}
}
for (Enumeration enm = excluded.keys(); enm.hasMoreElements();) {
properties.remove(enm.nextElement());
}
return excluded;
}
/**
* Filter/Eliminate keys that have a value that starts with the given prefix.
*
* @param properties
* @param prefix
* @return
*/
public static Properties filterValuesStartingWith(Properties properties, String prefix) {
if (!StringUtils.hasText(prefix))
return EMPTY_PROPERTIES;
Assert.notNull(properties);
Properties excluded = (properties instanceof OrderedProperties ? new OrderedProperties() : new Properties());
for (Enumeration enm = properties.keys(); enm.hasMoreElements();) {
String key = (String) enm.nextElement();
String value = properties.getProperty(key);
if (value.startsWith(prefix)) {
excluded.put(key, value);
}
}
for (Enumeration enm = excluded.keys(); enm.hasMoreElements();) {
properties.remove(enm.nextElement());
}
return excluded;
}
/**
* Apply placeholder expansion to the given properties object.
*
* Will return a new properties object, containing the expanded entries. Note that both keys and values will be
* expanded.
*
* @param props
* @return
*/
public static Properties expandProperties(Properties props) {
Assert.notNull(props);
Set entrySet = props.entrySet();
Properties newProps = (props instanceof OrderedProperties ? new OrderedProperties() : new Properties());
for (Iterator iter = entrySet.iterator(); iter.hasNext();) {
// first expand the keys
Map.Entry entry = (Map.Entry) iter.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
String resultKey = expandProperty(key, props);
String resultValue = expandProperty(value, props);
// replace old entry
newProps.put(resultKey, resultValue);
}
return newProps;
}
private static String expandProperty(String prop, Properties properties) throws IllegalArgumentException {
boolean hasPlaceholder = false;
String copy = prop;
StringBuilder result = new StringBuilder();
int index = 0;
// dig out the placeholders
do {
index = copy.indexOf(DELIM_START);
if (index >= 0) {
hasPlaceholder = true;
// add stuff before the delimiter
result.append(copy.substring(0, index));
// remove the delimiter
copy = copy.substring(index + DELIM_START.length());
// find ending delim
int stopIndex = copy.indexOf(DELIM_STOP);
String token = null;
if (stopIndex >= 0) {
// discover token
token = copy.substring(0, stopIndex);
// remove ending delimiter
copy = copy.substring(stopIndex + 1);
// append the replacement for the token
result.append(properties.getProperty(token));
}
else
throw new IllegalArgumentException("cannot interpret property " + prop + " due of token [" + copy
+ "]");
} else {
hasPlaceholder = false;
// make sure to append the remaining string
result.append(copy);
}
} while (hasPlaceholder);
return result.toString();
}
}